﻿using Microsoft.DirectX;
using System;
using System.Collections;
using System.IO;
using System.Text;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectSound;
using System.Drawing;

namespace WiiShooter
{
    class Entity
    {
        public Microsoft.DirectX.Direct3D.Device device;
        public Microsoft.DirectX.DirectSound.Device soundDevice;
        public Vector3 centerCoords;
        private Vector3 moveVector;
        public ArrayList spriteName;
        private ArrayList spriteTextures = new ArrayList();
        private ArrayList spriteDeathTextures = new ArrayList();
        private ArrayList spriteDeathTextureNames = new ArrayList();
        private ArrayList spriteShootTextures = new ArrayList();
        private ArrayList spriteShootTextureNames = new ArrayList();
        public CustomVertex.PositionNormalTextured[] spriteArray;
        public Texture texture;
        public ArrayList theSounds;
        public int theHealth;
        public float theSpeed;
        public int theType;
        public float theWidth;
        public float theHeight;
        public float theDepth;
        public float theScale;
        public float theAngle;
        private int shootTime;
        private int countTime;
        private Random randomNumGen;
        private String projectileName;
        public float[,] thePathArray;
        public float nodePointer, nodePointer2, prevNode;
        public String theName;
        public bool removeMe = false;
        private int animateIndex = 0;
        private int animationTime = 3;
        private int deathAnimationTime = 0;
        private int deathIndex = 0;
        private int shootIndex = 0;
        public Boolean hitX, hitY, hitZ;
        private BufferDescription buffer_desc = new BufferDescription();
        public SecondaryBuffer buffer;
        private String soundEffectName;
        private bool traversePath = true;
        public bool allowTraversal = false;
        public bool spawnEnemies = false;
        //public bool alreadyAccessed = false;
        public bool[] alreadyAccessed;
        public bool continueOn = true;
        public bool tempSemaphore = false;
        public bool setCurrentStageToZero;

        public Entity(float[,] pathArray, string textFileToLookAt, ref Microsoft.DirectX.Direct3D.Device devicePass,
            float anglePass, ref Microsoft.DirectX.DirectSound.Device soundDevicePass)
        {
            hitX = false;
            hitY = false;
            hitZ = false;
            device = devicePass;
            soundDevice = soundDevicePass;
            //initialize sound
            buffer_desc.ControlEffects = true; //this has to be true to use effects.
            buffer_desc.GlobalFocus = true; //play sound even if application loses focus.
            thePathArray = pathArray;
            theAngle = anglePass;
            theName = textFileToLookAt;
            spriteArray = new CustomVertex.PositionNormalTextured[6];
            centerCoords.X = thePathArray[0, 2];
            centerCoords.Y = thePathArray[0, 3];
            centerCoords.Z = thePathArray[0, 4];
            moveVector = new Vector3(0f, 0f, 0f);
            countTime = 0;
            randomNumGen = new Random();
            shootTime = randomNumGen.Next(10, 20);
            readFile(textFileToLookAt);
            alreadyAccessed = new bool[pathArray.Length];

        }

        /**
         * loadSounds - Loads the appropriate sounds for this entity type.
         */
        private void loadSounds()
        {
            buffer = new SecondaryBuffer(soundEffectName, buffer_desc, soundDevice);
        }

        /**
         * loadSprites - Loads the sprites for this entity type.
         */
        private void loadSprites()
        {
            /* put all the textures into memory */
            for (int i = 0; i < spriteName.Count; i++)
            {
                spriteTextures.Add(TextureLoader.FromFile(device, (string)spriteName[i]));
            }
            for (int i = 0; i < spriteDeathTextureNames.Count; i++)
            {
                spriteDeathTextures.Add(TextureLoader.FromFile(device, (string)spriteDeathTextureNames[i]));
            }
            for (int i = 0; i < spriteShootTextureNames.Count; i++)
            {
                spriteShootTextures.Add(TextureLoader.FromFile(device, (string)spriteShootTextureNames[i]));
            }
            texture = (Texture)spriteTextures[0];
        }

        /**
         * readFile - Reads text from a file.
         */
        public void readFile(string FileName)
        {
            StreamReader objReader = new StreamReader(FileName);
            string sLine = "";
            spriteName = new ArrayList();
            theSounds = new ArrayList();

            // Start read ye olde texte file(e).
            while(sLine != null)
            {
                sLine = objReader.ReadLine();
                if(sLine != null) {
                    if(sLine.StartsWith("Sprite")){
                        string[] inputs = sLine.Split(',');
                        for(int i = 1; i < inputs.Length; i++)
                            spriteName.Add(inputs[i]);
                    }
                    if (sLine.StartsWith("Death"))
                    {
                        string[] inputs = sLine.Split(',');
                        for (int i = 1; i < inputs.Length; i++)
                            spriteDeathTextureNames.Add(inputs[i]);
                    }
                    if (sLine.StartsWith("Shoot"))
                    {
                        string[] inputs = sLine.Split(',');
                        for (int i = 1; i < inputs.Length; i++)
                            spriteShootTextureNames.Add(inputs[i]);
                    }
                    if(sLine.StartsWith("Health")) {
                        string[] inputs = sLine.Split(',');
                        theHealth = int.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("Speed")) {
                        string[] inputs = sLine.Split(',');
                        theSpeed = float.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("Sound,")) {
                        string[] inputs = sLine.Split(',');
                        soundEffectName = inputs[1];
                    }
                    if(sLine.StartsWith("Projectile_type")) {
                        string[] inputs = sLine.Split(',');
                        projectileName = inputs[1];
                    }
                    if(sLine.StartsWith("Width")) {
                        string[] inputs = sLine.Split(',');
                        theWidth = int.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("Height")) {
                        string[] inputs = sLine.Split(',');
                        theHeight = int.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("Depth")) {
                        string[] inputs = sLine.Split(',');
                        theDepth = int.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("Scale")) {
                        string[] inputs = sLine.Split(',');
                        theScale = int.Parse(inputs[1]);
                    }
                    if(sLine.StartsWith("EntityType")) {
                        string[] inputs = sLine.Split(',');
                        theType = int.Parse(inputs[1]);
                    }
                }
            }
            objReader.Close();
            loadSprites();
            loadSounds();
        }

        /**
         * Death animation, displays a series of images, then sets this entity to be removed
         */ 
        private void death()
        {
            if (deathAnimationTime == 2){
                if (deathIndex < spriteDeathTextures.Count - 1){
                    deathIndex++;
                    deathAnimationTime = 0;
                }else{
                    removeMe = true;
                    Game.currentNumEnemies -= 1;
                    Console.WriteLine("currentNumEnemies: " + Game.currentNumEnemies);
                }
                texture = (Texture)spriteDeathTextures[deathIndex];
            }else{
                deathAnimationTime++;
            }
        }

        /**
         * Do any animation
         */ 
        private void animate()
        {
            if (animateIndex < spriteTextures.Count - 1)
            {
                animateIndex++;
            }
            else
            {
                animateIndex = 0;
            }
            texture = (Texture)spriteTextures[animateIndex];
        }

        /**
         * Shoot the player, with an animation too
         */ 
        private void shootPlayer()
        {
            if (shootIndex < spriteShootTextures.Count - 1) {
                shootIndex++;
            } else {
                if (!theName.Equals("Player.txt")) { //we don't want the player to shoot himself
                    Projectile monsterProjectile = new Projectile(this, projectileName, ref device,
                        ref soundDevice);
                    Game.projectileList.Add(monsterProjectile);
                    shootTime = randomNumGen.Next(30, 75);
                }
                shootIndex = 0;
                countTime = 0;
            }
            texture = (Texture)spriteShootTextures[shootIndex];
        }

        /**
         * This method calculates how much to move in the X, Y, and Z directions to arrive
         * at the same point at the same time.  Called only when the node changes
         */ 
        private void calculateMoveDist()
        {
            float xDiff = 0, yDiff = 0, zDiff = 0;
            nodePointer2 = thePathArray[(int)nodePointer, 1];

            if(theName.Equals("Player.txt")) {
                Console.WriteLine("current node: " + nodePointer + ", moving to: " + nodePointer2);
                tempSemaphore = false;
            }

            // Decided to set elements of 'alreadyAccessed[]' to false here so that it
            //   will loop through the stages again.
            if(nodePointer == 0 && nodePointer2 == 1) {
                for(int i = 0; i < alreadyAccessed.Length; i++) 
                {
                    alreadyAccessed[i] = false;
                }
                //Set currentStage to zero.
                setCurrentStageToZero = true;
            }
            /*if(this.theName.Equals("Player.txt")){
                Game.camera.cameraPosition.X = centerCoords.X = thePathArray[(int)nodePointer, 2];
                Game.camera.cameraPosition.Y = centerCoords.Y = thePathArray[(int)nodePointer, 3];
                Game.camera.cameraPosition.Z = centerCoords.Z = thePathArray[(int)nodePointer, 4];
            }*/
            /*Game.camera.cameraTarget.X = cameraPosition.X
            Game.camera.cameraTarget.Y = 0
            Int16.MaxValue*/


            xDiff = thePathArray[(int)nodePointer2, 2] - thePathArray[(int)nodePointer, 2];
            yDiff = thePathArray[(int)nodePointer2, 3] - thePathArray[(int)nodePointer, 3];
            zDiff = thePathArray[(int)nodePointer2, 4] - thePathArray[(int)nodePointer, 4];
            float distanceToTravel = (float)Math.Sqrt((double)xDiff * xDiff + yDiff * yDiff + zDiff * zDiff);

            if(xDiff != 0){
                moveVector.X = Math.Abs((xDiff / distanceToTravel)) * theSpeed;
            } else {
                moveVector.X = 0;
            }
            if(yDiff != 0){
                moveVector.Y = Math.Abs((yDiff / distanceToTravel)) * theSpeed;
            } else {
                moveVector.Y = 0;
            }
            if(zDiff != 0){
                moveVector.Z = Math.Abs((zDiff / distanceToTravel)) * theSpeed;
            } else {
                moveVector.Z = 0;
            }
        }

        /**
         * Method to move from one node to the next in the path
         */ 
        public void move()
        {
            float scaledSizeX, scaledSizeY; //scaled size in the X and Y dimensions
            scaledSizeX = theWidth * theScale; //used to display the scaled image
            scaledSizeY = theHeight * theScale;  //used to display the scaled image

            //are we dead yet?
            if (theHealth <= 0) {
                buffer.Play(0, BufferPlayFlags.Default);
                death();
            } else {
                //change the animation?
                if (animationTime == 3) {
                    animate();
                    animationTime = 0;
                } else {
                    animationTime++;
                }
                //is it time to shoot the player?
                if (countTime == shootTime) {
                    shootPlayer();
                } else {
                    countTime++;
                }
            }

            /*
            // Check to see if the player needs to stop in accordance with the path.
            if(theName.Equals("Player.txt") && thePathArray[(int)nodePointer, 5] == 1 &&
               traversePath && !alreadyAccessed[(int)nodePointer]) {
                // The player needs to stop.
                traversePath = false;
                spawnEnemies = true;
                alreadyAccessed[(int)nodePointer] = true;
                //continueOn = false;
            }

            if(!traversePath && allowTraversal) {
                traversePath = true;
            }
            */

            if(theName.Equals("Player.txt") && thePathArray[(int)nodePointer, 5] == 1 &&
               !tempSemaphore && !alreadyAccessed[(int)nodePointer] ) {
                continueOn = false;
                tempSemaphore = true;
                spawnEnemies = true;
                alreadyAccessed[(int)nodePointer] = true;
            }

            //if(traversePath){
            if(continueOn){
                //  The player does not need to stop.
                //this detects if the current entity position is equal to one of its nodes, in
                //which case it goes to the next node in the path
                if (Math.Abs((centerCoords.X - thePathArray[(int)nodePointer, 2])) < theSpeed &&
                    Math.Abs((centerCoords.Y - thePathArray[(int)nodePointer, 3])) < theSpeed &&
                    Math.Abs((centerCoords.Z - thePathArray[(int)nodePointer, 4])) < theSpeed)
                {
                    calculateMoveDist();
                    nodePointer = thePathArray[(int)nodePointer, 1];
                } else {
                    if (centerCoords.X > thePathArray[(int)nodePointer, 2]) {
                        if (theName.Equals("Player.txt")) {
                            Game.camera.moveCameraAlongPath(-moveVector.X, 0, 0);
                        } else {
                            centerCoords.X -= moveVector.X;
                        }
                    } else if (centerCoords.X < thePathArray[(int)nodePointer, 2]) {
                        if (theName.Equals("Player.txt")) {
                            Game.camera.moveCameraAlongPath(moveVector.X, 0, 0);
                        } else {
                            centerCoords.X += moveVector.X;
                        }
                    }
                    if (centerCoords.Y > thePathArray[(int)nodePointer, 3]) {
                        if (theName.Equals("Player.txt")) {
                            Game.camera.moveCameraAlongPath(0, -moveVector.Y, 0);
                        } else {
                            centerCoords.Y -= moveVector.Y;
                        }
                    } else if (centerCoords.Y < thePathArray[(int)nodePointer, 3]) {
                        if (theName.Equals("Player.txt")) {
                            Game.camera.moveCameraAlongPath(0, moveVector.Y, 0);
                        } else {
                            centerCoords.Y += moveVector.Y;
                        }
                    }
                    if (centerCoords.Z > thePathArray[(int)nodePointer, 4]) {
                        if (theName.Equals("Player.txt")) {
                            Game.camera.moveCameraAlongPath(0, 0, -moveVector.Z);
                        } else {
                            centerCoords.Z -= moveVector.Z;
                        }
                    } else if (centerCoords.Z < thePathArray[(int)nodePointer, 4]) {
                        if (theName.Equals("Player.txt")) {
                            Game.camera.moveCameraAlongPath(0, 0, moveVector.Z);
                        } else {
                            centerCoords.Z += moveVector.Z;
                        }
                    }
                }
            }

            Vector3 vector3one = new Vector3(centerCoords.X - (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y - (scaledSizeY / 2), centerCoords.Z - (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            Vector3 vector3two = new Vector3(centerCoords.X + (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y - (scaledSizeY / 2), centerCoords.Z + (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            Vector3 vector3three = new Vector3(centerCoords.X + (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y + (scaledSizeY / 2), centerCoords.Z + (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            Vector3 vector3four = new Vector3(centerCoords.X - (float)((scaledSizeX / 2) * Math.Cos((double)theAngle)),
                centerCoords.Y + (scaledSizeY / 2), centerCoords.Z - (float)((scaledSizeX / 2) * Math.Sin((double)theAngle)));
            spriteArray[0] = new CustomVertex.PositionNormalTextured(vector3four, new Vector3(0, 0, 1f), 0, 0);
            spriteArray[1] = new CustomVertex.PositionNormalTextured(vector3one, new Vector3(0, 0, 1f), 0, 1f);
            spriteArray[2] = new CustomVertex.PositionNormalTextured(vector3two, new Vector3(0, 0, 1f), 1f, 1f);
            spriteArray[3] = new CustomVertex.PositionNormalTextured(vector3two, new Vector3(0, 0, 1f), 1f, 1f);
            spriteArray[4] = new CustomVertex.PositionNormalTextured(vector3four, new Vector3(0, 0, 1f), 0, 0);
            spriteArray[5] = new CustomVertex.PositionNormalTextured(vector3three, new Vector3(0, 0, 1f), 1f, 0);
        }
    }
}
